<template>
  <view class="app-ellipsis__container">
    <text
      id="ellipsis-content"
      class="app-ellipsis__content"
    >
      {{ text }}
    </text>
    <text
      class="app-ellipsis__btn"
      v-if="isNeedEllipsisBtn"
      @click="handleEllipsis"
    >
      {{ isEllipsis ? extendText : ellipsisText }}
    </text>
  </view>
  <view
    id="temp-container"
    class="app-ellipsis__temp-container"
    :style="{ display: tempContainerDisplay }"
  >
    {{ tempText }}
  </view>
</template>

<script setup lang="ts">
interface Props {
  content: string
  lineHeight?: number
  rows?: number
  extendText?: string
  ellipsisText?: string
}
const props = withDefaults(defineProps<Props>(), {
  lineHeight: 30,
  rows: 3,
  extendText: '展开',
  ellipsisText: '收起'
})

const text = ref<string>('')
const tempText = ref<string>('')
const tempContainerDisplay = ref<'block' | 'none'>('block')
const lineHeightVal = ref<string>(`${props.lineHeight}px`)
const isNeedEllipsisBtn = ref<boolean>(true)
const isEllipsis = ref<boolean>(true)

const calcEllipsis = async () => {
  tempContainerDisplay.value = 'block'
  const getTempContainerStyle = async () => {
    await nextTick()
    const tempContainerStyle = await useSelectElement('#temp-container')
    if (Array.isArray(tempContainerStyle)) return
    return tempContainerStyle
  }
  const calcEllipsisText = async (maxHeight: number) => {
    const { content, extendText } = props
    const dot = '...'
    let left = 0
    let right = content.length
    let res = -1
    while (left <= right) {
      const mid = Math.floor((left + right) / 2)
      tempText.value = content.slice(0, mid) + dot + extendText
      const tempContainerStyle = await getTempContainerStyle()
      if (tempContainerStyle!.height! <= maxHeight) {
        left = mid + 1
        res = mid
      } else {
        right = mid - 1
      }
    }
    return content.slice(0, res) + dot
  }
  const maxHeight = (Number(props.rows) + 0.5) * props.lineHeight
  const tempContainerStyle = await getTempContainerStyle()
  if (maxHeight < tempContainerStyle!.height!) {
    isNeedEllipsisBtn.value = true
    text.value = await calcEllipsisText(maxHeight)
  } else {
    isNeedEllipsisBtn.value = false
    text.value = props.content
  }
  tempContainerDisplay.value = 'none'
}

const handleEllipsis = async () => {
  isEllipsis.value = !isEllipsis.value
  if (isEllipsis.value) {
    await calcEllipsis()
    return
  }
  text.value = props.content
}

watch(
  [() => props.content, () => props.rows],
  async () => {
    tempText.value = props.content
    await calcEllipsis()
  },
  { immediate: true }
)

const handleWindowResize = async () => {
  await calcEllipsis()
}

onMounted(async () => {
  await calcEllipsis()
  uni.onWindowResize(handleWindowResize)
})
onBeforeUnmount(() => {
  uni.offWindowResize(handleWindowResize)
})
</script>

<style lang="scss" scoped>
.app-ellipsis__container {
  width: 100%;
  .app-ellipsis__content {
    width: 100%;
    color: white;
  }
  .app-ellipsis__btn {
    margin-top: 12rpx;
    color: lightblue;
    cursor: pointer;
  }
}
.app-ellipsis__temp-container {
  position: fixed;
  z-index: -9999;
  top: -9999px;
  height: auto;
  line-height: v-bind(lineHeightVal);
  min-height: auto;
  max-height: auto;
}
</style>
